import WebpackLicense from '@components/WebpackLicense';
import { Collapse, CollapsePanel } from '@components/Collapse';
import { Tab, Tabs, PackageManagerTabs } from '@theme';
import { Stability } from '@components/ApiMeta';
import CompilerType from '../../types/compiler.mdx';
import WatchingType from '../../types/watching.mdx';
import StatsType from '../../types/stats.mdx';

<WebpackLicense from="https://webpack.docschina.org/api/node/" />

# JavaScript API

Rspack 提供了一组 JavaScript API，可在 Node.js、Deno、Bun 等 JavaScript 运行时中使用。

当你需要自定义构建或开发流程时，JavaScript API 非常有用，因为此时所有的报告和错误处理都必须自行实现，Rspack 仅仅负责编译的部分。所以 [`stats`](/config/stats) 配置项不会在 `rspack()` 调用中生效。

:::tip 提示
`@rspack/core` 是对齐 webpack 的 JavaScript API 设计的，旨在提供一致的功能和相似的使用体验。
:::

## 安装

开始使用 Rspack 的 JavaScript API 之前，首先你需要安装 `@rspack/core`：

<PackageManagerTabs command="add @rspack/core -D" />

在 JavaScript 文件中，引入 `@rspack/core` 模块：

<Tabs>
  <Tab label="ESM">

```js title="build.mjs"
import { rspack } from '@rspack/core';
```

  </Tab>
  <Tab label="CJS">

```js title="build.cjs"
const { rspack } = require('@rspack/core');
```

  </Tab>
</Tabs>

## rspack()

导入的 `rspack` 函数会将`配置对象`传给 Rspack，如果同时传入回调函数会在 Rspack compiler 运行时被执行：

```js
import { rspack } from '@rspack/core';

rspack({}, (err, stats) => {
  if (err || stats.hasErrors()) {
    // ...
  }
  // 处理完成
});
```

```ts
function rspack(
  options: MultiRspackOptions | RspackOptions,
  callback?: Callback<Error, MultiStats | Stats>,
): null | MultiCompiler | Compiler;
```

:::tip 提示
`err` 对象**不包含**编译错误，必须使用 `stats.hasErrors()` 单独处理，文档的[错误处理](/api/javascript-api/index#错误处理)将对这部分进行详细介绍。`err` 对象只包含 Rspack 相关的问题，例如配置错误等。
:::

:::tip 提示
你也可以为 rspack() 函数提供一个配置数组。更多详细信息，请查看 [MultiCompiler](/api/javascript-api/index#multicompiler) 章节。
:::

## Compiler 实例

如果你不向 `rspack` 传入可执行的回调函数，它会返回一个 Rspack `Compiler` 实例。你可以通过手动执行它或者为它的构建时添加一个监听器，就像 [CLI](/api/cli) 类似。Compiler 实例提供了以下方法：

- `.run(callback)`
- `.watch(watchOptions, handler)`

通常情况下，仅会创建一个主要 `Compiler` 实例，虽然可以创建一些子 compiler 来代理到特定任务。`Compiler` 基本上只是执行最低限度的功能，以维持生命周期运行的功能。它将所有的加载、打包和写入工作，都委托到注册过的插件上。

`Compiler` 实例上的 `hooks` 属性，用于将一个插件注册到 `Compiler` 的生命周期中的所有钩子事件上。Rspack 使用 [`RspackOptionsApply`](https://github.com/web-infra-dev/rspack/blob/main/packages/rspack/src/rspackOptionsApply.ts) 来配置 `Compiler` 实例以及所有内置插件。

> 关于 Compiler 的更多细节请参考 [Compiler API](/api/javascript-api/compiler)。

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Compiler.ts"
    key="compiler"
  >
    <CompilerType />
  </CollapsePanel>
</Collapse>

## compiler.run

使用 `run` 方法启动所有编译工作。完成之后，执行传入的的 `callback` 函数。最终记录下来的概括信息（stats）和错误（errors），都应在这个 callback 函数中获取。

```js
import { rspack } from '@rspack/core';

const compiler = rspack({
  // ...
});

compiler.run((err, stats) => {
  // ...

  compiler.close(closeErr => {
    // ...
  });
});
```

:::warning 警告
该 API 不支持并发编译。当你使用 `run` 或 `watch` 时，请先调用 `close` 并等待其完成，然后再调用 `run` 或 `watch`。并发编译会破坏输出文件。
:::

## compiler.watch

调用 `watch` 方法会触发 Rspack 执行，但之后会监听变更（很像 CLI 命令： `rspack --watch`），一旦 Rspack 检测到文件变更，就会重新执行编译。该方法返回一个 `Watching` 实例。

```js
import { rspack } from '@rspack/core';

const compiler = rspack({
  // ...
});

const watching = compiler.watch(
  {
    // 示例
    aggregateTimeout: 300,
    poll: undefined,
  },
  (err, stats) => {
    // 这里打印 watch/build 结果...
    console.log(stats);
  },
);
```

`Watching` 配置选项的细节可以在[这里](/config/watch#watchoptions)查询。

:::warning 警告
文件系统不正确的问题，可能会对单次修改触发多次构建。因此，在上面的示例中，一次修改可能会多次触发 `console.log` 语句。用户应该预知此行为，并且可能需要检查 `stats.hash` 来查看文件哈希是否确实变更。
:::

> 关于 Watching 的更多细节请参考 [`Compiler.watch`](/api/javascript-api/compiler#watch)。

<Collapse>
  <CollapsePanel
    className="collapse-code-panel"
    header="Watching.ts"
    key="watching"
  >
    <WatchingType />
  </CollapsePanel>
</Collapse>

## Stats

`stats` 对象会被作为 [`rspack()`](/api/javascript-api/index#rspack) 回调函数的第二个参数传递，可以通过它获取到代码编译过程中的有用信息，包括：

- 错误和警告（如果有的话）
- 计时信息
- 模块和 chunk 信息

[Rspack CLI](/api/cli) 正是基于这些信息在控制台展示友好的格式输出。

:::tip 提示
当使用 `MultiCompiler` 时，会返回一个 `MultiStats` 实例，它实现与 `stats` 相同的接口，也就是下面描述的方法。
:::

> 关于 Stats 对象更多细节请参考 [Stats API](/api/javascript-api/stats)。

<Collapse>
  <CollapsePanel className="collapse-code-panel" header="Stats.ts" key="stats">
    <StatsType />
  </CollapsePanel>
</Collapse>

## MultiCompiler

`MultiCompiler` 模块可以让 Rspack 同时执行多个配置。如果传给 Rspack 的 JavaScript API 的 `options` 参数，该参数由是由多个配置对象构成的数组，Rspack 会相应地创建多个 compiler 实例，并且在所有 compiler 执行完毕后调用 `callback` 方法。

```js
import { rspack } from '@rspack/core';

rspack(
  [
    { entry: './index1.js', output: { filename: 'bundle1.js' } },
    { entry: './index2.js', output: { filename: 'bundle2.js' } },
  ],
  (err, stats) => {
    process.stdout.write(stats.toString() + '\n');
  },
);
```

> 关于 MultiCompiler 对象更多细节请参考 [MultiCompiler API](/api/javascript-api/compiler#multicompiler)。

## 错误处理

完备的错误处理中需要考虑以下三种类型的错误:

- 致命的 Rspack 错误（配置出错等）
- 编译错误（缺失的模块，语法错误等）
- 编译警告

下面是一个覆盖这些场景的示例:

```js
import { rspack } from '@rspack/core';

rspack(
  {
    // ...
  },
  (err, stats) => {
    if (err) {
      console.error(err.stack || err);
      if (err.details) {
        console.error(err.details);
      }
      return;
    }

    const info = stats.toJson();

    if (stats.hasErrors()) {
      console.error(info.errors);
    }

    if (stats.hasWarnings()) {
      console.warn(info.warnings);
    }

    // Log result...
  },
);
```

## 自定义文件系统

:::danger 与 webpack 的差异

1. 当前 Rspack 对于 `inputFileSystem` 的支持存在限制，尚未实现与 webpack 一致的自定义文件系统读取能力。请参考：[Issue #5091](https://github.com/web-infra-dev/rspack/issues/5091)。

2. Rspack 在指定的输出文件系统，不再需要提供 `mkdirp` 和 `join` 工具方法。

:::

默认情况下，Rspack 使用普通文件系统来读取文件并将文件写入磁盘。但是，还可以使用不同类型的文件系统（内存, webDAV 等）来更改输入或输出行为。为了实现这一点，可以改变 `inputFileSystem` 或 `outputFileSystem`。例如，可以使用 [`memfs`](https://github.com/streamich/memfs) 替换默认的 `outputFileSystem`，以将文件写入到内存中，而不是写入到磁盘：

```js
import { createFsFromVolume, Volume } from 'memfs';
import { rspack } from '@rspack/core';

const fs = createFsFromVolume(new Volume());
const compiler = rspack({
  /* 配置选项 */
});

compiler.outputFileSystem = fs;
compiler.run((err, stats) => {
  // 之后读取输出：
  const content = fs.readFileSync('...');
  compiler.close(closeErr => {
    // ...
  });
});
```

## `sources` 对象

`@rspack/core` 通过 `sources` 导出了 [webpack-sources](https://github.com/webpack/webpack-sources) 模块。它提供了一组 class，用于创建和操作源代码片段和源码映射。在开发 Rspack 插件时，你可以使用这些 class 来处理和操作源代码。

```js
import { sources } from '@rspack/core';

const { RawSource } = sources;
const source = new RawSource('console.log("Hello, world!");');
```

详细用法请参考 [webpack-sources](https://github.com/webpack/webpack-sources) 文档。
